/* SPI interface commands */

/* Copyright (C) 2009-2011 David Zanetti
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/* This implements the setup/use of the hardware SPI interface
   but does not handle the !CS line of any SPI-connected chip. */

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include <avr/eeprom.h>

#include "global.h"
#include <util/delay.h>

#include "spi.h"
#include "serial.h"

/* which SPI port are we using here */
#define SPI_HW SPID
#define SPI_PORT PORTD

void spi_init(void) {
	/* ensure the MOSI, SCK, !SS pins are outputs, always pin 4, 5 and 7 on the given port */
	/* !SS has to be output or any pulldown on it will force-disable master mode */
	SPI_PORT.DIRSET = PIN4_bm | PIN5_bm | PIN7_bm;

	/* fixme, abstract this */
	/* power up SPI module */
	PR.PRPD &= ~(PR_SPI_bm);

    /* enable hardware, master, SPI mode 0, perclk/4 */
	SPI_HW.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm;
}

uint8_t spi_txrx(uint8_t c) {
	uint8_t in;

#if defined(DEBUG_SPI)
	serial0_tx_PGM(PSTR("spi: "));
	serial0_tx_hex(c);
#endif

	SPI_HW.DATA = c;
	while (!(SPI_HW.STATUS & SPI_IF_bm));
	in = SPI_HW.DATA;

#if defined(DEBUG_SPI)
	serial0_tx_PGM(PSTR(" "));
	serial0_tx_hex(in);
	serial0_tx_cr();
#endif

	return in;
}

void spi_slow_mode1(void) {
	SPI_HW.CTRL |= SPI_PRESCALER_DIV16_gc | (SPI_MODE_1_gc); /* set to per/16, SPI Mode 1 */
	return;
}

void spi_fast_mode0(void) {
	SPI_HW.CTRL &= ~(SPI_PRESCALER_gm | SPI_MODE_gm); /* set per/4, SPI Mode 0 */
	return;
}

